/*
 * Composition Knowledge Miner. A data mashup composition knowledge miner
 * Copyright (C) 2011 Carlos Rodriguez
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package eu.ict_omelette.mashup.compositionminer;

import eu.ict_omelette.mashup.adapters.pipes.PipesAdapter;
import eu.ict_omelette.mashup.canonicalmodel.CanonicalMashup;
import eu.ict_omelette.mashup.canonicalmodel.Component;
import eu.ict_omelette.mashup.canonicalmodel.Dataflow;
import eu.ict_omelette.mashup.canonicalmodel.InputParameter;
import eu.ict_omelette.mashup.compositionminer.util.Carpenter;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author Carlos Rodriguez
 */
public class ConnectorPatternMiner {
    private LinkedList<CanonicalMashup> compRepo;
    private static String SEPARATOR = "&nbsp;";
    private static String EQUAL = ":=>";
    
    public ConnectorPatternMiner(LinkedList<CanonicalMashup> compRepo){
        this.compRepo = compRepo;
    }

    public List<CanonicalMashup> mine(Double minSupDf, Double minSupDm){
        LinkedList<CanonicalMashup> freqDf = new LinkedList();

        LinkedList<LinkedList> db = new LinkedList();
        LinkedHashMap<String, LinkedList> hashRecConn = Util.getRecurrentConnectors(compRepo, minSupDf);
        
        //System.out.println("\t\t" + hashRecConn.size());


        
        LinkedList<Dataflow> conInst = Util.getConnectorInstances(this.compRepo, hashRecConn);

        //Here we construct Canonical Mashups that contains only the frequent
        //dataflow and the corresponding pair of components
        Iterator<Dataflow> itConIns = conInst.iterator();
        while(itConIns.hasNext()){
            Dataflow df = itConIns.next();
            Component toComp = df.getToComponent();
            Component fromComp = df.getFromComponent();

            CanonicalMashup cm = new CanonicalMashup();
            cm.setId("");
            cm.setName("");
            cm.setSrc("");

            cm.addComponent(fromComp);
            cm.addComponent(toComp);

            cm.addDataflow(df);

            freqDf.add(cm);
        }
        
        
        freqDf = getFrequentDataMappings(freqDf, minSupDf);

        /*
        //The following block of code is just to compute some statistics
        freqDf = new LinkedList();
        Iterator it = hashRecConn.keySet().iterator();
        while(it.hasNext()){
            it.next();
            freqDf.add(new CanonicalMashup());
        }
         */

        return freqDf;
        
    }

    //This method creates a signature for a dataflow in the form of:
    //componentX=>ComponentY
    private String getDfSignature(CanonicalMashup cm){
        
        Iterator<Dataflow> itDf = cm.getDataFlows();
        LinkedHashMap<String, String> dfSigs = new LinkedHashMap();
        while(itDf.hasNext()){
            Dataflow df = itDf.next();
            String sourceComp = df.getFromComponent().getType();
            String targetComp = df.getToComponent().getType();
            String sig = sourceComp + "=>" + targetComp;
            dfSigs.put(sig, sig);
        }
               
        LinkedList lst = new LinkedList();
        lst.addAll(dfSigs.keySet());

        Collections.sort(lst);

        String sig = "";
        for(int i = 0; i < lst.size(); i++){
            sig += lst.get(i) + ",";
        }

        //System.out.println(sig);

        return sig;
    }

    private String getStrDataMappingsAssig(Component comp, boolean isEmbedded){
        String strVa = "";
        Iterator<InputParameter> itIp = comp.getInputParameters();
        while(itIp.hasNext()){
            InputParameter ip = itIp.next();

            //If the input parameter is a "data mapped" parameter
            if(ip.getDataMapping().equals(PipesAdapter.TRUE) &&
               (!ip.getValue().equals(""))){

                strVa += comp.getType() + "." + ip.getName() + EQUAL + ip.getValue() + SEPARATOR;

            }
        }

        //System.out.println(strVa);

        return strVa;
    }

    private LinkedList<CanonicalMashup> getFrequentDataMappings(LinkedList<CanonicalMashup> freqRepo, double minSupVa){
        LinkedHashMap<String, LinkedList> lhmSig = new LinkedHashMap();
        LinkedList<CanonicalMashup> resRepo = new LinkedList();

        //Here, we put in hash map all the canonical models, indexed by the
        //signature returned by the function *getDfSignature()*
        for(int i = 0; i < freqRepo.size(); i++){
            CanonicalMashup cm = freqRepo.get(i);
            String sig = getDfSignature(cm);

            LinkedList ll = lhmSig.get(sig);
            if(ll == null){
                ll = new LinkedList();
                ll.add(cm);
            }else{
                ll.add(cm);
            }
            lhmSig.put(sig, ll);
        }

        //here we iterate over all the signatures we have and then mine
        //parameter value patterns from the models that correspond to the
        //same signature
        Iterator<String> itSig = lhmSig.keySet().iterator();
        while(itSig.hasNext()){
            String sig = itSig.next();
            LinkedList<CanonicalMashup> ll = lhmSig.get(sig);
            LinkedList<String> strDB = new LinkedList();

            //We get the value assignments for the input parameters of the component
            Iterator<CanonicalMashup> itCm = ll.iterator();
            while(itCm.hasNext()){
                CanonicalMashup cm = itCm.next();
                String strVa = "";

                Iterator<Component> itComp = cm.getComponents();
                while(itComp.hasNext()){
                    Component comp = itComp.next();
                    strVa = strVa + getStrDataMappingsAssig(comp, false);
                }

                strVa = strVa.trim();
                if(!strVa.equals("")){
                    strDB.add(strVa);
                    //System.out.println(strVa);
                }
                
                //System.out.println(strVa);
            }

            LinkedHashMap<String, Integer> hashToNbr = new LinkedHashMap();
            LinkedHashMap<Integer, String> hashToStr = new LinkedHashMap();
            int idx = 0;
            String[] tranDB = new String[strDB.size()];

            for(int i = 0; i < strDB.size(); i++){
                String line = strDB.get(i);
                String[] elemVec = line.split(SEPARATOR);
                tranDB[i] = "";
                //System.out.println("\n");

                for(int j = 0; j < elemVec.length; j++){

                    String elem = elemVec[j];

                    if(hashToNbr.get(elem) == null){
                        hashToNbr.put(elem, idx);
                        hashToStr.put(idx, elem);
                        idx++;
                    }
                    //System.out.print(hashToNbr.get(elem) + " ");

                    tranDB[i] += hashToNbr.get(elem) + " ";

                }

                //System.out.println();

                tranDB[i] = tranDB[i].trim();

                //System.out.println(tranDB[i]);

            }

            try{

                //System.out.println("\n\nRESULT:");
                Carpenter car = new Carpenter(tranDB, minSupVa);
                List<int[]> listPatterns = car.mine();


                //We take one exemplary canonical mashup for the current signature and
                //clean up the parameter values before re-filling them with
                //the *frequent* parameter values
                CanonicalMashup cmClean = ll.get(0);
                Iterator<Component> itComp = cmClean.getComponents();
                while(itComp.hasNext()){
                    Component comp = itComp.next();
                    Iterator<InputParameter> itIp = comp.getInputParameters();
                    while(itIp.hasNext()){
                        InputParameter ip = itIp.next();

                        //if(ip.getDataMapping().equals(PipesAdapter.TRUE)){
                            ip.setValue("");
                        //}
                    }

                }


                //System.out.println("######### " + sig);

                //And here we assign the parameter values that are frequent
                //to the canonical mashups
                for(int i = 0; i < listPatterns.size(); i++){
                    int[] line = listPatterns.get(i);

                    //We clone() the clean version of the canonical model
                    CanonicalMashup cm = cmClean.clone();

                    for(int j=0; j < line.length; j++){

                        //System.out.print(line[j] + "  ");

                        String sign = (String)hashToStr.get(line[j]);

                        Iterator<Component> itCo = cm.getComponents();
                        while(itCo.hasNext()){
                            Component comp = itCo.next();

                            String[] pair = sign.split(EQUAL);
                            String param = pair[0];
                            String value = "";

                            if(pair.length == 2)
                                value=pair[1];

                           
                            Iterator<InputParameter> itIp = comp.getInputParameters();
                            while(itIp.hasNext()){
                                InputParameter ip = itIp.next();

                                if(param.contains(comp.getType()) && param.contains(ip.getName())){
                                    ip.setValue(value);
                                }
                            }

                        }

                    }

                    //System.out.println("");

                    resRepo.add(cm);
                }


                /*
                System.out.println("\n\n");

                for(int k = 0; k < freqRepo.size(); k++){
                    CanonicalMashup cm = freqRepo.get(k);
                    Util.printCanonicalMashup(cm);
                }
                 */


                /*
                //DEBUGGING CODE
                for(int i = 0; i < listPatterns.size(); i++){
                    int[] line = listPatterns.get(i);

                    for(int j=0; j < line.length; j++){
                        System.out.println(hashToStr.get(line[j]));
                        //System.out.print(line[j] + " ");
                    }

                    System.out.println();
                }
                 */





            }catch(Exception e){

                e.printStackTrace();

            }



        }

        return resRepo;

    }
    

}
